/* + C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* See top source directory for COPYRIGHT.txt                              */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/* - C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "athd.h"

static void * _athd_cmd_handler_thread(void *ctx);
static apr_status_t _athd_cmd_handler_lock();
static apr_status_t _athd_cmd_handler_unlock();
static long _athd_cmd_handler_sessions_active_add();
static int _athd_cmd_handler_sessions_active_rm();
static long _athd_cmd_handler_request_add();
static void _athd_cmd_handler_destroy(athd_cmd_handler **handler);
static void _athd_cmd_handler_thread_cleanup(void *ctx);

static int _sessions_active = 0;
static long _sessions_total = 0;
static long _requests_total = 0;
static apr_pool_t *_handler_pool = NULL;
static pthread_mutex_t *_handler_mutex = NULL;


void
athd_cmd_handler_shared_setup(apr_pool_t *pglobal)
{
    ASSERT(!_handler_pool);
    apr_pool_create(&_handler_pool, pglobal);
    ASSERT(!_handler_mutex);
    _handler_mutex = apr_pcalloc(_handler_pool, sizeof(pthread_mutex_t));
    apr_pool_cleanup_register(_handler_pool, _handler_mutex, 
			      pthread_mutex_cleanup, apr_pool_cleanup_null);
    pthread_mutex_init(_handler_mutex, NULL);
}

athd_cmd_handler *
athd_cmd_handler_create(apr_pool_t *pool)
{
    apr_pool_t *subpool;
    athd_cmd_handler *handler;
    apr_pool_create(&subpool, pool);
    handler = apr_pcalloc(subpool, sizeof(athd_cmd_handler));
    /* used for handler session lifetime allocs */
    handler->pool = subpool;
    handler->thread = apr_pcalloc(handler->pool, sizeof(pthread_t));
    handler->thread_func = _athd_cmd_handler_thread;
    handler->socket = NULL;
    handler->prompt = NULL;
    handler->id = -1;
    handler->cmd_ctx = NULL;
    return handler;
}


int
athd_cmd_handler_sessions_active()
{
    int count;
    _athd_cmd_handler_lock();
    count = _sessions_active;
    _athd_cmd_handler_unlock();
    return count;
}

long
athd_cmd_handler_sessions_total()
{
    long count;
    _athd_cmd_handler_lock();
    count = _sessions_total;
    _athd_cmd_handler_unlock();
    return count;
}

long
athd_cmd_handler_requests_total()
{
    long count;
    _athd_cmd_handler_lock();
    count = _requests_total;
    _athd_cmd_handler_unlock();
    return count;
}

static void *
_athd_cmd_handler_thread(void *ctx)
{
    athd_cmd_handler *handler;
    int rv = APR_SUCCESS;
    char *strrecv;
    athd_cmd_act action = ATHD_ACT_CONTINUE;
    int req_count = 0;
    apr_pool_t *req_pool;

    ASSERT(ctx);
    handler = (athd_cmd_handler *)ctx;
    ASSERT(handler);
    ASSERT(handler->pool);
    pthread_cleanup_push((void*)_athd_cmd_handler_thread_cleanup, 
			 (void *)handler);
    handler->id = _athd_cmd_handler_sessions_active_add();
    handler->type = ATHD_CMD_STREAM; /* default to stream mode */
    apr_pool_create(&req_pool, handler->pool);
    while ((!rv) && (action == ATHD_ACT_CONTINUE))
      {
	  athd_cmd_req *req;
	  req = athd_cmd_req_create(req_pool, handler);
	  rv = athd_cmd_io_recv_str(req, &strrecv);
	  if (strrecv)
	    {
		req_count++;
		_athd_cmd_handler_request_add();
		action = athd_cmd_parser(req, strrecv);
	    }
	  else
	      action = ATHD_ACT_STOP;
	  apr_pool_clear(req_pool);
      }
    if ((rv) && (!APR_STATUS_IS_ECONNRESET(rv)))
	athd_log_err(ATHD_WARN, rv, "Cmd handler thread exit: ");
    _athd_cmd_handler_sessions_active_rm();
    if (req_pool)
	apr_pool_destroy(req_pool);
    pthread_cleanup_pop(1);
}

/* returns a number to be used as id */
static long 
_athd_cmd_handler_sessions_active_add()
{
    long count;
    _athd_cmd_handler_lock();
    _sessions_active++;
    count = ++_sessions_total;
    _athd_cmd_handler_unlock();
    return count;
}

static int 
_athd_cmd_handler_sessions_active_rm()
{
    int count;
    _athd_cmd_handler_lock();
    count = --_sessions_active;
    _athd_cmd_handler_unlock();
    return count;
}

static long 
_athd_cmd_handler_request_add()
{
    long count;
    _athd_cmd_handler_lock();
    count = ++_requests_total;
    _athd_cmd_handler_unlock();
    return count;
}


static apr_status_t
_athd_cmd_handler_lock()
{
    ASSERT(_handler_mutex);
    return pthread_mutex_lock(_handler_mutex);
}

static apr_status_t
_athd_cmd_handler_unlock()
{
    ASSERT(_handler_mutex);
    return pthread_mutex_unlock(_handler_mutex);
}

static void
_athd_cmd_handler_destroy(athd_cmd_handler **handler)
{
    apr_pool_t *pool;
    ASSERT(*handler);
    DEBUGOUT("handler destroy: %d", *handler);
    pool = (*handler)->pool;
    apr_pool_destroy(pool);
    *handler = NULL;
}


static void
_athd_cmd_handler_thread_cleanup(void *ctx)
{
    athd_cmd_handler *handler = (athd_cmd_handler *)ctx;
    ASSERT(handler);
    DEBUGOUT("handler exit: %d", handler);
    if (handler->socket)
	apr_socket_close(handler->socket);
    _athd_cmd_handler_destroy(&handler);
}
